package broker_manager;

import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URI;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

import broker.Broker;
import broker.BrokerImpl.BrokerEventType;

import common.BrokerRef;
import common.DefaultRMISecurityManager;
import common.NetworkParameters;

public class BrokerManagerImpl extends UnicastRemoteObject implements
		BrokerManager {

	private static final long serialVersionUID = 1L;
	private ArrayList<BrokerRef> brokers = new ArrayList<BrokerRef>();
	private URI localURI;
	private boolean networkUpdating = false;

	public BrokerManagerImpl() throws RemoteException {
		super();
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new DefaultRMISecurityManager());
		}
	}

	@Override
	public ArrayList<URI> getBrokers() {
		ArrayList<URI> list = new ArrayList<URI>();
		for (BrokerRef br : brokers) {
			list.add(br.uri);
		}
		return list;
	}

	@Override
	public void networkUpdateComplete() {

		// Re-enable the possibility for brokers to register/unregister
		networkUpdating = false;
	}

	@Override
	public ArrayList<URI> registerBroker(URI newBrokerURI) throws Exception,
			MalformedURLException, RemoteException, NotBoundException {

		// If the network is busy updating, reject the registration request
		if (networkUpdating) {
			System.out.println("Registration attempt from " + newBrokerURI
					+ " refused");
			return null;
		} else {
			networkUpdating = true;
		}

		// Check for duplicated brokers
		if (brokers.contains(new BrokerRef(newBrokerURI, null)))
			throw new Exception("Duplicated broker");
		brokers.add(new BrokerRef(newBrokerURI, (Broker) Naming.lookup("//"
				+ newBrokerURI.toString() + "/Broker")));

		// Notify to all other brokers about the new one
		for (BrokerRef br : brokers) {
			if (br.uri.equals(newBrokerURI))
				continue;
			br.broker.beginNetworkReorganization(newBrokerURI,
					BrokerEventType.COMING);
		}
		System.out.println("Broker " + newBrokerURI.toString() + " registered");
		return getBrokers();
	}

	public void start(String networkInterface) throws RemoteException,
			MalformedURLException, UnknownHostException, SocketException {
		Naming.rebind("BrokerManager", this);
		localURI = NetworkParameters.getLocalURI(networkInterface);
		System.out.println("Broker manager started on " + localURI);
	}

	@Override
	public boolean unregisterBroker(URI brokerURI) throws Exception,
			MalformedURLException, RemoteException, NotBoundException {

		// If the network is busy updating, reject the unregistration request
		if (networkUpdating) {
			System.out.println("Unregistration attempt from " + brokerURI
					+ " refused");
			return false;
		} else {
			networkUpdating = true;
		}

		// Check if the unregistering broker was registered
		if (!brokers.contains(new BrokerRef(brokerURI, null)))
			throw new Exception("Not registered broker");

		// Remove the leaving broker from the list
		brokers.remove(new BrokerRef(brokerURI, null));
		System.out.println("Broker " + brokerURI.toString() + " unregistered");

		// Notify to all other brokers
		for (BrokerRef br : brokers)
			br.broker.beginNetworkReorganization(brokerURI,
					BrokerEventType.LEAVING);

		// If no more brokers are active, the networkUpdating variable will
		// never be set to false because no one starts a network update: set it
		// to false here.
		if (brokers.isEmpty())
			networkUpdating = false;

		return true;
	}
}
